/***************************************************************************
 *   Copyright (C) 2005 by xihe						   *
 *   xihels@163.com							   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "BuddyList.h"

#include <gtkmm/treeselection.h>
#include "sounds.h"
#include "pixmaps.h"
#include "MainWindow.h"

const char SexMM[] = "MM Age:";
const char SexGG[] = "GG Age:";
const char SexUnknow[] = "^_* Age:";


#define _ONLINE	    1
#define _AWAY	    2
#define _OFFLINE    3

#define GET_STATUS(A) (QQ_FRIEND_STATUS_ONLINE == (A)) ? _ONLINE : (QQ_FRIEND_STATUS_AWAY == (A) ? _AWAY : _OFFLINE)

void BuddyList::RefreshFriendsStatus::operator() (const Gtk::TreeRow& row)
{
    QQSession::Friends::value_type value = 
	list.getElement(row[(wnd.columns).uid]);

    if ((NULL != value) && 
	    (row[wnd.columns.status] !=
	    GET_STATUS(value->getStatus()))) {

	row[wnd.columns.face] = 
	    getPix(value->getFace(), value->getStatus());

	row[wnd.columns.status] =
	    GET_STATUS(value->getStatus());
	value->isShowed(true);
    }

}

    template<typename T>		
void BuddyList::RefreshFriends<T>::operator () (const Gtk::TreeRow& row)
{
    typename T::value_type value = 
	list.getElement(row[(wnd.columns).uid]);
    if (NULL != value)
	wnd.setIter(row, *value, session);
}

    template<typename T>
void BuddyList::AddFriends<T>::operator () (typename T::value_type value)
{
    if ( BuddyList::isShow(value, group)) {
	Gtk::TreeModel::iterator treeiter = 
	    wnd.refTreeStore->append(iter->children());
	wnd.setIter(*treeiter, *value, session);

    }
}

BuddyList::BuddyList(MainWindow& _parent):
    parent(_parent),
    userMenu(_parent),
    groupMenu(_parent),
    buddyMenu(_parent)
{

    refTreeStore = Gtk::TreeStore::create(columns);

    this->set_model(refTreeStore);

    Gtk::TreeView::Column* col = Gtk::manage( new Gtk::TreeView::Column("好友") );
    
    col->pack_start(columns.face, false); //false = don't expand.
    col->pack_start(columns.name, false); //false = don't expand.

    vector<Gtk::CellRenderer*> rends = col->get_cell_renderers();
    col->clear_attributes(*rends[1]);
    col->add_attribute(*rends[1], "markup", 1);

    this->append_column(*col);

    refTreeStore->set_default_sort_func(sigc::mem_fun(*this, &BuddyList::on_sort_compare));
    refTreeStore->set_sort_column_id(
	    Gtk::TreeSortable::DEFAULT_SORT_COLUMN_ID,
	    Gtk::SORT_ASCENDING);
}

void BuddyList::setIter(const Gtk::TreeRow& row,
	GroupInfo& groupinfo, int session)
{

    char* marktext;
    if (!groupinfo.getNickname().empty()) {
	marktext = g_markup_printf_escaped("<b>%s</b>", groupinfo.getNickname().c_str());
    } else {
	marktext = g_markup_printf_escaped("<b>%d</b>", groupinfo.getUid());
    }
    
    row[columns.name] = marktext;
    g_free(marktext);	// C free

    row[columns.uid]  = groupinfo.getUid();
    row[columns.face] = getGroupFace22();
    row[columns.type] = TREE_ITEM_GROUP;
    row[columns.status] = _ONLINE;
    row[columns.session]   = session;
    groupinfo.isShowed(true);

}

void BuddyList::setIter(const Gtk::TreeRow& row,
	ClusterInfo& clusterinfo, int session)
{

    char* marktext;
    if (!clusterinfo.getNickname().empty()) {
	marktext = g_markup_printf_escaped("%s", clusterinfo.getNickname().c_str());
    } else {
	marktext = g_markup_printf_escaped("%d", clusterinfo.getUid());
    }

    row[columns.name] = marktext;
    g_free(marktext);	// C free

    row[columns.uid]  = clusterinfo.getUid();
    row[columns.face] = getClusterFace();
    row[columns.type] = TREE_ITEM_CLUSTER;
    row[columns.status] = _ONLINE;
    row[columns.session]   = session;
    clusterinfo.isShowed(true);
}

void BuddyList::setIter(const Gtk::TreeRow& row,
	FriendInfo& friendinfo, int session)
{

    char* marktext;
    if (!friendinfo.getNickname().empty()) {
	marktext = g_markup_printf_escaped(
		"%s\n<span color='dim grey'><small>%s%d</small></span>",
		friendinfo.getNickname().c_str(), 
		(0 == friendinfo.getSex()) ? SexGG : 
		(1 == friendinfo.getSex() ? SexMM : SexUnknow),
		friendinfo.getAge());
    } else {
	marktext = g_markup_printf_escaped(
		"%s\n<span color='dim grey'><small>%s%d</small></span>",
		friendinfo.getNickname().c_str(), 
		(friendinfo.getSex()) ? SexGG :
		(1 == friendinfo.getSex() ? SexMM : SexUnknow),
		friendinfo.getAge());
    }
    row[columns.name] = marktext;

    g_free(marktext);	// free

    row[columns.uid]  = friendinfo.getUid();
    row[columns.face] = 
	getPix(friendinfo.getFace(), friendinfo.getStatus());

    row[columns.type] = TREE_ITEM_BUDDY;
    row[columns.status] = GET_STATUS(friendinfo.getStatus());
    row[columns.session]   = session;
    friendinfo.isShowed(true);
}

void BuddyList::removeUser(uint32_t uid)
{
    // 查找要删除的好友列表是否已经显示
	Gtk::TreeModel::iterator iter = getListIter(refTreeStore->children(), uid);
	if (iter != refTreeStore->children().end())
	    refTreeStore->erase(iter);
}



Gtk::TreeModel::iterator BuddyList::addUser(const QQSession& session)
{
    // 列表头

    Gtk::TreeModel::iterator listiter = refTreeStore->append(); 
    
    char* marktext;
    if (QQ_FRIEND_STATUS_ONLINE == session.getCurrentStatus())
	marktext = g_markup_printf_escaped(
	        "<b>%s(%d)</b>",
		session.getNickname().c_str(), session.getUid());
    else
	marktext = g_markup_printf_escaped(
		"<span color='dim grey'><b>%s(%d)</b></span>",
		session.getNickname().c_str(), session.getUid());

    (*listiter)[columns.name] = marktext;
    g_free(marktext);	// C free
    
    (*listiter)[columns.uid]  = session.getUid();
    (*listiter)[columns.face] = 
	getPix22(session.getFace() / 3, session.getCurrentStatus());
    (*listiter)[columns.type] = TREE_ITEM_USER;
    (*listiter)[columns.status] = _ONLINE;
    (*listiter)[columns.session] = reinterpret_cast<long>(&session);

    // 群列表头
    Gtk::TreeModel::iterator treeiter = refTreeStore->append(listiter->children());

    (*treeiter)[columns.name] = "<b>我的群</b>";
    (*treeiter)[columns.uid] = CLUSTER_GROUP_NUM;
    (*treeiter)[columns.face] = getClusterFace22();
    (*treeiter)[columns.type] = TREE_ITEM_GROUP;
    (*treeiter)[columns.status] = _ONLINE;
    (*treeiter)[columns.session] = reinterpret_cast<long>(&session);

    // 默认好友列表头
    treeiter = refTreeStore->append(listiter->children());
    (*treeiter)[columns.name] = "<b>我的好友</b>";
    (*treeiter)[columns.uid] = DEFAULT_GROUP;
    (*treeiter)[columns.face] = getGroupFace22();
    (*treeiter)[columns.type] = TREE_ITEM_GROUP;
    (*treeiter)[columns.status] = _ONLINE;
    (*treeiter)[columns.session] = reinterpret_cast<long>(&session);

    this->expand_row(Gtk::TreeModel::Path(listiter), true);
    return listiter;
}    

void BuddyList::refreshSelfStatus(const QQSession& session)
{
    // 查找要更新的好友列表是否已经显示
    Gtk::TreeModel::Children children = refTreeStore->children();
    Gtk::TreeModel::iterator listiter = 
	find_if(children.begin(),
		children.end(),
		bind2nd(CompareBuddy(columns), session.getUid()));
    if (listiter == children.end())
	listiter = addUser(session);
    else {
	
	char* marktext;
	if (QQ_FRIEND_STATUS_ONLINE == session.getCurrentStatus())
	    marktext = g_markup_printf_escaped(
		    "<b>%s(%d)</b>",
		    session.getNickname().c_str(), session.getUid());
	else
	    marktext = g_markup_printf_escaped(
		    "<span color='dim grey'><b>%s(%d)</b></span>",
		    session.getNickname().c_str(), session.getUid());

	(*listiter)[columns.name] = marktext;
	g_free(marktext);	// C free

	(*listiter)[columns.face] = 
	    getPix22(session.getFace() / 3, session.getCurrentStatus());
    }

}
void BuddyList::refreshGroupsList(const QQSession& session, int refreshtype)
{
    // 刷新好友分组表头 
    // 查找要更新的好友列表是否已经显示
    Gtk::TreeModel::Children children = refTreeStore->children();
    Gtk::TreeModel::iterator listiter = 
	find_if(children.begin(),
		children.end(),
		bind2nd(CompareBuddy(columns), session.getUid()));

    // 没有显示，加入列表头
    if(listiter == children.end()) {
	listiter = addUser(session);
    }

    refreshFriendsList(listiter,
	    session.getGroups(), 
	    refreshtype, 
	    reinterpret_cast<long>(&session));
}

template <typename T>
void BuddyList::refreshFriendsList(
	Gtk::TreeModel::iterator listhead,
	const T& list,
	int refreshtype,
	int session)
{
    typename T::value_type value;

    Gtk::TreeModel::Children child = listhead->children();

    if (refreshtype & REFRESH_TREEITEM)
	for_each(child.begin(), child.end(), RefreshFriends<T>(*this, list, session));

    // 插入剩下的

    if (refreshtype & ADD_TREEITEM)
	list.forEach(AddFriends<T>(*this, child, session));

}
void BuddyList::refreshClustersList(const QQSession& session, int refreshtype)
{

    // 查找群列表
	Gtk::TreeModel::iterator useriter =
	    getListIter(refTreeStore->children(), session.getUid());
	if (refTreeStore->children().end() == useriter)
	    return;

	Gtk::TreeModel::iterator clustersiter =
	    getListIter((*useriter).children(), CLUSTER_GROUP_NUM);
	if ((*useriter).children().end() == clustersiter)
	    return;

	// 刷新群列表
	const QQSession::Clusters& clusters = 
	    session.getClusters();
	refreshFriendsList(clustersiter,
		clusters,
		refreshtype, 
		reinterpret_cast<long>(&session));

}

void BuddyList::refreshFriendsListStatus(const QQSession& session)
{
    // 刷新好友列表状态

	Gtk::TreeModel::iterator useriter =
	    getListIter(refTreeStore->children(), session.getUid());
	if (refTreeStore->children().end() == useriter)
	    return;

	Gtk::TreeModel::Children grouplist = (*useriter).children();

	Gtk::TreeModel::iterator groupiter = grouplist.begin();
	Gtk::TreeModel::iterator last      = grouplist.end();

	if (groupiter == last) {
	    fprintf(stderr, "refreshFriendsListStatus error: can not find group\n");
	    return;
	}

	++groupiter;

	// 刷新分组好友列表
	const QQSession::Friends& friends = 
	    session.getFriends();

	// 暂时取消排序
	refTreeStore->set_sort_column_id(-2, Gtk::SORT_ASCENDING);

	for (; groupiter != last; ++groupiter) {
	    Gtk::TreeModel::Children child = groupiter->children();
	    for_each(child.begin(), child.end(),
		    RefreshFriendsStatus(*this, friends));
	}
	// 排序
	refTreeStore->set_sort_column_id(
		Gtk::TreeSortable::DEFAULT_SORT_COLUMN_ID,
		Gtk::SORT_ASCENDING);

}
void BuddyList::refreshFriendsList(const QQSession& session, int refreshtype)
{
    // 刷新好友列表

	Gtk::TreeModel::iterator useriter =
	    getListIter(refTreeStore->children(), session.getUid());
	if (refTreeStore->children().end() == useriter)
	    return;

	Gtk::TreeModel::Children grouplist = (*useriter).children();

	Gtk::TreeModel::iterator groupiter = grouplist.begin();
	Gtk::TreeModel::iterator last      = grouplist.end();

	if (groupiter == last) {
	    fprintf(stderr, "refreshFriendsList error: can not find group\n");
	    return;
	}

	++groupiter;

	// 刷新分组好友列表
	const QQSession::Friends& friends = 
	    session.getFriends();

	// 暂时取消排序
	refTreeStore->set_sort_column_id(-2, Gtk::SORT_ASCENDING);

	for (; groupiter != last; ++groupiter) {
	    refreshFriendsList(groupiter,
		    friends,
		    refreshtype,
		    reinterpret_cast<long>(&session));
	}
	// 排序
	refTreeStore->set_sort_column_id(
		Gtk::TreeSortable::DEFAULT_SORT_COLUMN_ID,
		Gtk::SORT_ASCENDING);
}

void BuddyList::refreshFriendStatus(const QQSession& session, FriendInfo& friendinfo)
{
    // 刷新好友状态
    // 刷新好友
	Gtk::TreeModel::iterator iter =
	    getListIter(refTreeStore->children(), session.getUid());
	
	if (refTreeStore->children().end() == iter)
	    return;

	Gtk::TreeModel::iterator lastiter = (*iter).children().end();
	iter = getListIter((*iter).children(), friendinfo.getGroup());
	
	if (iter == lastiter)
	    return;
	
	lastiter = (*iter).children().end();
	iter = getListIter(iter->children(), friendinfo.getUid());
	if (iter == lastiter)
	    return;

	int status1 = (*iter)[columns.status];
	int status2 = GET_STATUS(friendinfo.getStatus());
	if (status1 != status2) {
	    if (_ONLINE == status1)
		sounds::play(sounds::LEAVE_SOUND);
	    else 
		sounds::play(sounds::ARRIVE_SOUND);
	}

	(*iter)[columns.status] = 
	    getPix(friendinfo.getFace(), friendinfo.getStatus());

	(*iter)[columns.status] =
	    GET_STATUS(friendinfo.getStatus());
}    


void BuddyList::refreshFriend(const QQSession& session,
	FriendInfo& friendinfo)
{
    // 刷新好友
	Gtk::TreeModel::iterator iter =
	    getListIter(refTreeStore->children(), session.getUid());
	if (refTreeStore->children().end() == iter)
	    return;
	
	Gtk::TreeModel::iterator lastiter = (*iter).children().end();
	iter = getListIter((*iter).children(), friendinfo.getGroup());
	if (lastiter == iter)
	    return;
	
	lastiter = (*iter).children().end();
	iter = getListIter((*iter).children(), friendinfo.getUid());
	if (lastiter == iter)
	    return;

	int status1 = (*iter)[columns.status];
	int status2 = GET_STATUS(friendinfo.getStatus());
	if (status1 != status2) {
	    if (_OFFLINE == status2)
		sounds::play(sounds::LEAVE_SOUND);
	    else if (_OFFLINE == status1)
		sounds::play(sounds::ARRIVE_SOUND);
	}

	setIter(*iter, friendinfo, reinterpret_cast<long>(&session));
}

void BuddyList::refreshCluster(const QQSession& session,
	ClusterInfo& clusterinfo)
{
    // 刷新群列表

	Gtk::TreeModel::iterator iter = getListIter(refTreeStore->children(), session.getUid());
	if (refTreeStore->children().end() == iter)
	    return;
	
	Gtk::TreeModel::iterator lastiter = (*iter).children().end();
	iter = getListIter((*iter).children(), CLUSTER_GROUP_NUM);
	if (iter == lastiter)
	    return;
	
	Gtk::TreeModel::iterator clusteriter = find_if((*iter).children().begin(), 
		(*iter).children().end(),
		bind2nd(CompareBuddy(columns),
		    clusterinfo.getUid()));

	if (clusteriter == (*iter).children().end()) {
	    clusteriter = refTreeStore->append((*iter).children());
	}

	setIter(*clusteriter, clusterinfo, reinterpret_cast<long>(&session));
}

Gtk::TreeModel::iterator BuddyList::getListIter(
	Gtk::TreeModel::Children children, int id)
{
    // 查找好友或组列表项
    return find_if(children.begin(),
		children.end(),
		bind2nd(CompareBuddy(columns), id));
}


bool BuddyList::on_button_press_event(GdkEventButton *ev)
{
    bool result = Gtk::TreeView::on_button_press_event(ev);

    Glib::RefPtr<Gtk::TreeSelection> selection = this->get_selection();	
    Gtk::TreeModel::iterator iter = selection->get_selected();
    if (!selection->count_selected_rows())
	return result;

    int type = (*iter)[columns.type];

    if ( (ev->type==GDK_2BUTTON_PRESS ||
		ev->type==GDK_3BUTTON_PRESS) ) {
	Gtk::TreeModel::Path path(iter);
	switch (type) {
	    case TREE_ITEM_CLUSTER:
	    case TREE_ITEM_BUDDY:
		GUnit::getMainWindow().on_buddyChat_activate();
		break;
	    default:
		if (this->row_expanded(path))
		    this->collapse_row(path);
		else
		    this->expand_row(path, false);
		break;
	}

    } else if ((ev->type == GDK_BUTTON_PRESS)
	    && (ev->button == 3)) {

	int tmp = (*iter)[columns.session];
	QQSession* session = reinterpret_cast<QQSession*>(tmp);
	if (NULL != session) {
	    switch (type) {
		case TREE_ITEM_CLUSTER:
		case TREE_ITEM_BUDDY:
		    buddyMenu.popup(1, gtk_get_current_event_time());
		    break;
		case TREE_ITEM_GROUP:
		    groupMenu.popup(1, gtk_get_current_event_time());
		    break;
		case TREE_ITEM_USER:
		    userMenu.popup(1, gtk_get_current_event_time());
		    break;
	    } 
	}
    }
    return result;
}


void BuddyList::on_cursor_changed()
{
    GUnit::getMainWindow().on_buddyList_row_changed();
}
